import aiosqlite
from typing import Optional
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
import asyncio
from threading import Thread, Event
from queue import Queue, Empty
import json
import hashlib
import uuid
from datetime import datetime, timedelta
import sys
import os
from typing import Optional
from src.custom import PROJECT_ROOT
import PIL.Image
import PIL.ImageTk
# 添加项目根目录到Python路径
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))

# 修复导入路径
from src.application.TikTokDownloader import TikTokDownloader
from src.tools.console import ColorfulConsole
from src.record.base import BaseLogger
from src.module.cookie import Cookie


class GUILogger(BaseLogger):
    """GUI日志记录器，将日志同时显示在GUI界面中"""
    
    def __init__(self, main_path, console, root="", folder="", name="", log_callback=None):
        super().__init__(main_path, console, root, folder, name)
        self.log_callback = log_callback
    
    def info(self, text: str, output=True, **kwargs):
        super().info(text, output, **kwargs)
        if self.log_callback and output:
            self.log_callback(text.strip())
    
    def warning(self, text: str, output=True, **kwargs):
        super().warning(text, output, **kwargs)
        if self.log_callback and output:
            self.log_callback(text.strip())
    
    def error(self, text: str, output=True, **kwargs):
        super().error(text, output, **kwargs)
        if self.log_callback and output:
            self.log_callback(text.strip())
    
    def debug(self, text: str, **kwargs):
        super().debug(text, **kwargs)
        if self.log_callback and super().DEBUG:
            self.log_callback(text.strip())


class TikTokGUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("李多多抖音下载器（最新视频+免水印+批量更新下载，技术研究...")
        self.root.geometry("800x800")
        self.root.resizable(True, True)
        
        # 使用状态变量
        self.is_authorized = self.check_authorization()
        
        # 设置自定义图标
        self.set_custom_icon()
        
        # 状态变量
        self.is_running = False
        self.download_thread: Optional[Thread] = None
        self.stop_event = Event()
        self.message_queue = Queue()
        self.app_instance = None  # 保存 TikTokDownloader 实例
        
        # 下载记录相关
        self.download_records = []
        self.selected_record = None
        
        # 创建界面
        self.create_widgets()
        
        # 定期检查消息队列
        self.check_queue()
        
    # 数据库相关方法
    async def get_download_records(self, limit=100, filter_text=""):
        """从数据库获取下载记录"""
        db_path = PROJECT_ROOT.joinpath("DouK-Downloader.db")
        if not db_path.exists():
            return []
        
        try:
            async with aiosqlite.connect(db_path) as conn:
                conn.row_factory = aiosqlite.Row
                cursor = await conn.cursor()
                
                # 构建查询语句
                query = "SELECT ID, FILE_PATH, FILE_NAME, CREATE_TIME FROM download_data ORDER BY CREATE_TIME DESC LIMIT ?"
                params = [limit]
                
                # 如果有过滤文本，则添加WHERE条件
                if filter_text:
                    query = "SELECT ID, FILE_PATH, FILE_NAME, CREATE_TIME FROM download_data WHERE FILE_NAME LIKE ? ORDER BY CREATE_TIME DESC LIMIT ?"
                    params = [f"%{filter_text}%", limit]
                
                await cursor.execute(query, params)
                rows = await cursor.fetchall()
                
                # 转换为字典列表
                records = []
                for row in rows:
                    records.append({
                        "id": row["ID"],
                        "file_path": row["FILE_PATH"],
                        "file_name": row["FILE_NAME"],
                        "create_time": row["CREATE_TIME"]
                    })
                
                return records
        except Exception as e:
            self.log_message(f"获取下载记录失败: {str(e)}")
            return []

    def load_download_records(self):
        """加载并显示下载记录"""
        # 在新线程中执行异步操作
        def run_async():
            records = asyncio.run(self.get_download_records(filter_text=self.record_filter_var.get()))
            # 更新UI需要在主线程中执行
            self.root.after(0, self.update_records_display, records)
        
        Thread(target=run_async, daemon=True).start()

    def update_records_display(self, records):
        """更新记录显示"""
        # 清空现有记录
        for item in self.records_tree.get_children():
            self.records_tree.delete(item)
        
        # 插入新记录
        for record in records:
            self.records_tree.insert("", "end", values=(
                record["id"],
                record["file_path"] or "",
                record["file_name"] or "",
                record["create_time"] or ""
            ))
        
        self.download_records = records

    def schedule_record_refresh(self):
        """定时刷新下载记录"""
        self.load_download_records()
        self.root.after(10000, self.schedule_record_refresh)  # 每10秒刷新一次

    def on_record_filter_change(self, *args):
        """过滤条件变化时重新加载记录"""
        self.load_download_records()

    def on_record_select(self, event):
        """选择记录时保存选中项"""
        selection = self.records_tree.selection()
        if selection:
            item = self.records_tree.item(selection[0])
            values = item['values']
            if values:
                self.selected_record = {
                    "id": values[0],
                    "file_path": values[1],
                    "file_name": values[2],
                    "create_time": values[3]
                }

    def open_selected_file(self):
        """打开选中的文件所在目录"""
        selection = self.records_tree.selection()
        if not selection:
            self.log_message("请先选择一条记录")
            return
        
        item = self.records_tree.item(selection[0])
        values = item['values']
        if not values or not values[1]:  # 检查文件路径是否存在
            self.log_message("选中的记录没有文件路径信息")
            return
        
        file_path = values[1]
        # 使用 os.path.exists 检查文件路径
        if not os.path.exists(file_path):
            self.log_message("文件不存在")
            return
        
        try:
            import subprocess
            import platform
            
            system = platform.system()
            if system == "Windows":
                # 使用explorer.exe打开目录并选中文件
                subprocess.Popen(f'explorer /select,"{file_path}"')
            elif system == "Darwin":  # macOS
                # 使用open命令选中文件
                subprocess.Popen(["open", "-R", file_path])
            else:  # Linux和其他UNIX系统
                # 获取文件所在的目录
                directory = os.path.dirname(file_path)
                
                # 确保目录路径有效
                if not directory:
                    directory = os.path.abspath(file_path)
                
                # 使用xdg-open打开目录
                subprocess.Popen(["xdg-open", directory])
        except Exception as e:
            self.log_message(f"打开文件目录失败: {str(e)}")
            # 记录详细的错误信息
            import traceback
            self.log_message(f"错误详情: {traceback.format_exc()}")

    def get_machine_code(self):
        """
        获取机器码
        """
        # 获取机器唯一标识
        machine_info = ""
        
        # 获取系统信息
        import platform
        machine_info += platform.node()  # 计算机名
        machine_info += platform.processor()  # 处理器信息
        machine_info += platform.system()  # 系统名
        machine_info += platform.release()  # 系统版本
        
        # 获取MAC地址
        import uuid
        mac = uuid.getnode()
        machine_info += str(mac)
        
        # 生成MD5哈希作为机器码
        machine_code = hashlib.md5(machine_info.encode('utf-8')).hexdigest()
        return machine_code[:16]  # 取前16位作为机器码

    def generate_license_key(self, machine_code, expiry):
        """
        生成使用码
        :param machine_code: 机器码
        :param days: 使用天数
        :return: 使用码
        """
        # 创建使用信息
        # expiry_date = datetime.now() + timedelta(days=days)
        license_data = {
            "machine_code": machine_code,
            "expiry": expiry,
            "generated_at": "2023-09-01T00:00:00+00:00liduos@#SSSDSFDsww"
        }
        
        # 将使用信息转换为字符串并生成哈希
        license_str = json.dumps(license_data, sort_keys=True)
        license_key = hashlib.sha256(license_str.encode('utf-8')).hexdigest().upper()
        return f"{expiry}-LICENSE-{license_key[:4]}-{license_key[4:8]}-{license_key[8:12]}-{license_key[12:20]}"

    def check_authorization(self):
        """
        检查使用状态
        返回True表示已使用，False表示未使用
        """
        # 检查使用文件是否存在
        auth_file = PROJECT_ROOT.joinpath("auth.key")
        if not auth_file.exists():
            return False
        
        try:
            # 读取使用文件
            with open(auth_file, 'r') as f:
                auth_data = json.load(f)
            
            # 检查机器码是否匹配
            if "machine_code" in auth_data:
                current_machine_code = self.get_machine_code()
                if auth_data["machine_code"] != current_machine_code:
                    return False

            if not self.validate_license_key(auth_data["license_key"], current_machine_code):
                    return False

            # 检查使用是否过期
            expiry_date = datetime.fromisoformat(auth_data["license_key"].split("-LICENSE")[0])
            if datetime.now() > expiry_date:
                return False
            
            return True
        except:
            return False
    
    def get_expiry_date(self):
        """
        获取使用研究时间
        """
        auth_file = PROJECT_ROOT.joinpath("auth.key")
        if not auth_file.exists():
            return None
        
        try:
            with open(auth_file, 'r') as f:
                auth_data = json.load(f)
            
            return datetime.fromisoformat(auth_data["license_key"].split("-LICENSE")[0])
        except:
            return None
    
    def set_custom_icon(self):
        """设置自定义图标"""
        try:
            # 查找图标文件路径
            icon_path = os.path.join(PROJECT_ROOT, "static", "images", "liduoduo.png.ico")
            if os.path.exists(icon_path):
                self.root.iconbitmap(icon_path)
            else:
                # 如果ICO文件不存在，尝试使用PNG文件
                png_icon_path = os.path.join(PROJECT_ROOT, "static", "images", "liduoduo.png")
                if os.path.exists(png_icon_path):
                    image = PIL.Image.open(png_icon_path)
                    photo = PIL.ImageTk.PhotoImage(image)
                    self.root.iconphoto(True, photo)
        except Exception as e:
            # 如果设置图标失败，只打印日志不中断程序
            print(f"设置自定义图标失败: {e}")

    def create_widgets(self):
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky="nsew")
        
        # 配置网格权重
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        main_frame.rowconfigure(5, weight=1)
        
        # 标题
        title_label = ttk.Label(main_frame, text="李多多抖音下载器（最新视频+免水印+批量更新下载，技术研究...", font=("Arial", 12, "bold"))
        title_label.grid(row=0, column=0, columnspan=3, pady=(0, 10))
        
        # 使用状态显示
        auth_frame = ttk.Frame(main_frame)
        auth_frame.grid(row=1, column=0, columnspan=2, pady=(0, 10), sticky="ew")
        
        auth_status = "已使用" if self.is_authorized else "未使用"
        self.auth_label = ttk.Label(auth_frame, text=f"使用状态: {auth_status}")
        self.auth_label.pack(side=tk.LEFT)
        
        if self.is_authorized:
            expiry_date = self.get_expiry_date()
            if expiry_date:
                days_left = (expiry_date - datetime.now()).days
                expiry_text = f"使用研究时间: {expiry_date.strftime('%Y-%m-%d %H:%M:%S')} (剩余{days_left}天)"
                ttk.Label(auth_frame, text=expiry_text, foreground="green").pack(side=tk.LEFT, padx=(10, 0))
        else:
            # 未使用时显示机器码
            machine_code = self.get_machine_code()
            machine_code_label = ttk.Label(auth_frame, text=f"机器码: {machine_code}", foreground="blue")
            machine_code_label.pack(side=tk.LEFT, padx=(10, 0))
            
            # 添加复制机器码按钮
            def copy_machine_code():
                self.root.clipboard_clear()
                self.root.clipboard_append(machine_code)
                self.log_message("机器码已复制到剪贴板")
                
            copy_button = ttk.Button(auth_frame, text="复制机器码", command=copy_machine_code)
            copy_button.pack(side=tk.LEFT, padx=(5, 0))
            
            ttk.Label(auth_frame, text="请联系管理员获取使用", foreground="red").pack(side=tk.LEFT, padx=(10, 0))
            # 添加激活使用按钮
            activate_button = ttk.Button(auth_frame, text="激活使用", command=self.activate_license)
            activate_button.pack(side=tk.RIGHT)

        # 礼品信息显示（支持文本选择）
        gift_frame = ttk.Frame(main_frame)
        gift_frame.grid(row=1, column=2, columnspan=1, pady=(0, 10), sticky="ew")
        gift_text = tk.Text(gift_frame, height=1, width=55, wrap=tk.WORD, bd=0, padx=0, pady=0)
        gift_text.insert(tk.END, "(交流微信：niejintong668888)获取图片批量尺寸修改工具...")
        gift_text.config(state=tk.DISABLED, bg=self.root.cget('bg'), relief=tk.FLAT, 
                         font=('TkDefaultFont', 9))
        gift_text.pack(side=tk.RIGHT)
        
        # 读取当前配置
        from src.config.settings import Settings
        from src.tools import ColorfulConsole
        settings = Settings(PROJECT_ROOT, ColorfulConsole())
        config_data = settings.read()
        
        # 读取当前配置
        from src.config.settings import Settings
        from src.tools import ColorfulConsole
        settings = Settings(PROJECT_ROOT, ColorfulConsole())
        config_data = settings.read()
        
        # 参数设置框架
        settings_frame = ttk.LabelFrame(main_frame, text="参数设置", padding="10")
        settings_frame.grid(row=2, column=0, columnspan=3, pady=(0, 10), sticky="ew")
        settings_frame.columnconfigure(1, weight=1)
        settings_frame.columnconfigure(3, weight=1)
        
        # recent_time_filter 设置（转换为分钟显示）
        ttk.Label(settings_frame, text="最近多久发布的视频(分钟):").grid(row=0, column=0, sticky="w", padx=(0, 5))
        # 将秒转换为分钟显示
        seconds = config_data.get("recent_time_filter", 24 * 60 * 60)
        minutes = seconds // 60
        self.recent_time_filter_var = tk.StringVar(value=str(minutes))
        ttk.Entry(settings_frame, textvariable=self.recent_time_filter_var, width=15).grid(row=0, column=1, sticky="w", padx=(0, 10))
        
        # duration_filter 设置
        ttk.Label(settings_frame, text="视频时长不超过:").grid(row=0, column=2, sticky="w", padx=(10, 5))
        self.duration_filter_var = tk.StringVar(value=config_data.get("duration_filter", "00:20:00"))
        ttk.Entry(settings_frame, textvariable=self.duration_filter_var, width=15).grid(row=0, column=3, sticky="w", padx=(0, 10))
        
        # save_path 设置
        ttk.Label(settings_frame, text="视频保存路径:").grid(row=1, column=0, sticky="w", padx=(0, 5), pady=(5, 0))
        self.save_path_var = tk.StringVar(value=config_data.get("save_path", str(PROJECT_ROOT)))
        if not self.save_path_var.get():  # 如果保存路径为空，则设置为当前目录
            self.save_path_var.set(str(PROJECT_ROOT))
        ttk.Entry(settings_frame, textvariable=self.save_path_var).grid(row=1, column=1, columnspan=2, sticky="ew", pady=(5, 0))
        
        # 保存设置按钮
        save_settings_button = ttk.Button(settings_frame, text="保存设置", command=self.save_settings)
        save_settings_button.grid(row=1, column=3, pady=(5, 0), sticky="e")
        
        # 控制按钮框架
        button_frame = ttk.Frame(main_frame)
        button_frame.grid(row=3, column=0, columnspan=3, pady=(0, 10), sticky="ew")
        
        # 开始按钮
        self.start_button = ttk.Button(button_frame, text="开始", command=self.start_download)
        self.start_button.pack(side=tk.LEFT, padx=(0, 5))
        # 根据使用状态设置按钮是否可用
        if not self.is_authorized:
            self.start_button.config(state=tk.DISABLED)
        
        # 停止按钮
        self.stop_button = ttk.Button(button_frame, text="停止", command=self.stop_download, state=tk.DISABLED)
        self.stop_button.pack(side=tk.LEFT, padx=(0, 5))
        
        # 清屏按钮
        clear_button = ttk.Button(button_frame, text="清屏", command=self.clear_log)
        clear_button.pack(side=tk.LEFT, padx=(0, 5))
        
        # 打开存储目录按钮
        open_folder_button = ttk.Button(button_frame, text="打开视频存储目录", command=self.open_project_folder)
        open_folder_button.pack(side=tk.LEFT)
        
        # 设置Cookie按钮
        set_cookie_button = ttk.Button(button_frame, text="设置关联", command=self.set_cookie)
        set_cookie_button.pack(side=tk.LEFT)
        
        # 监视地址按钮
        monitor_button = ttk.Button(button_frame, text="监视地址", command=self.manage_accounts)
        monitor_button.pack(side=tk.LEFT, padx=(5, 0))
        
        # 下载记录框架
        records_frame = ttk.LabelFrame(main_frame, text="下载记录 (最新100条)", padding="10")
        records_frame.grid(row=4, column=0, columnspan=3, pady=(0, 10), sticky="nsew")
        records_frame.columnconfigure(0, weight=1)
        records_frame.rowconfigure(1, weight=1)
        
        # 配置主框架的行权重，使记录框架能够随窗口调整大小
        main_frame.rowconfigure(4, weight=1)
        
        # 过滤器框架
        filter_frame = ttk.Frame(records_frame)
        filter_frame.grid(row=0, column=0, columnspan=2, sticky="ew", pady=(0, 5))
        filter_frame.columnconfigure(1, weight=1)
        
        ttk.Label(filter_frame, text="文件名过滤:").pack(side=tk.LEFT)
        self.record_filter_var = tk.StringVar()
        self.record_filter_var.trace('w', self.on_record_filter_change)
        filter_entry = ttk.Entry(filter_frame, textvariable=self.record_filter_var)
        filter_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(5, 5))
        
        refresh_button = ttk.Button(filter_frame, text="刷新", command=self.load_download_records)
        refresh_button.pack(side=tk.LEFT)
        
        # 创建Treeview来显示下载记录
        columns = ("ID", "文件路径", "文件名", "创建时间")
        self.records_tree = ttk.Treeview(records_frame, columns=columns, show="headings", height=8)
        
        # 定义列标题
        self.records_tree.heading("ID", text="ID")
        self.records_tree.heading("文件路径", text="文件路径")
        self.records_tree.heading("文件名", text="文件名")
        self.records_tree.heading("创建时间", text="创建时间")
        
        # 设置列宽
        self.records_tree.column("ID", width=100)
        self.records_tree.column("文件路径", width=200)
        self.records_tree.column("文件名", width=200)
        self.records_tree.column("创建时间", width=150)
        
        # 添加滚动条
        records_scroll_y = ttk.Scrollbar(records_frame, orient=tk.VERTICAL, command=self.records_tree.yview)
        records_scroll_x = ttk.Scrollbar(records_frame, orient=tk.HORIZONTAL, command=self.records_tree.xview)
        self.records_tree.configure(yscrollcommand=records_scroll_y.set, xscrollcommand=records_scroll_x.set)
        
        # 布局Treeview和滚动条
        self.records_tree.grid(row=1, column=0, sticky="nsew")
        records_scroll_y.grid(row=1, column=1, sticky="ns")
        records_scroll_x.grid(row=2, column=0, sticky="ew")
        
        # 绑定选择事件
        self.records_tree.bind("<<TreeviewSelect>>", self.on_record_select)
        
        # 添加打开文件按钮到同一行
        open_file_button = ttk.Button(filter_frame, text="定位选中文件", command=self.open_selected_file)
        open_file_button.pack(side=tk.LEFT, padx=(5, 0))
        
        # 日志显示区域
        self.log_text = scrolledtext.ScrolledText(main_frame, wrap=tk.WORD, height=15)
        self.log_text.grid(row=5, column=0, columnspan=3, sticky="nsew")
        self.log_text.configure(state=tk.DISABLED)
        # 允许选择文本
        self.log_text.bind("<Button-1>", lambda event: self.log_text.focus_set())
        
        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.grid(row=6, column=0, columnspan=3, sticky="ew", pady=(10, 0))
        
        # 初始化下载记录
        self.load_download_records()
        
        # 设置定时刷新 (每10秒)
        self.schedule_record_refresh()
    
    def start_download(self):
        """开始下载任务"""
        if not self.is_running:
            self.is_running = True
            self.start_button.config(state=tk.DISABLED)
            self.stop_button.config(state=tk.NORMAL)
            self.status_var.set("正在运行...")
            self.log_message("开始运行李多多下载器...仅供学习研究使用！！！")
            self.stop_event.clear()
            # 在新线程中运行下载器
            self.download_thread = Thread(target=self._run_downloader)
            self.download_thread.daemon = True
            self.download_thread.start()
    
    def stop_download(self):
        """停止下载任务"""
        if self.is_running:
            self.log_message("正在停止...")
            self.stop_event.set()
            
            # 设置 TikTokDownloader 实例的 running 标志为 False
            if self.app_instance:
                self.app_instance.running = False
                self.app_instance = None
            
            self.is_running = False
            self.start_button.config(state=tk.NORMAL)
            self.stop_button.config(state=tk.DISABLED)
            self.status_var.set("已停止")
    
    def save_settings(self):
        """保存设置到配置文件"""
        # 添加确认对话框
        from tkinter import messagebox
        result = messagebox.askyesno("确认修改", "确定要保存当前设置吗？")
        if not result:
            return
        
        try:
            # 读取当前配置
            from src.config.settings import Settings
            from src.tools import ColorfulConsole
            settings = Settings(PROJECT_ROOT, ColorfulConsole())
            config_data = settings.read()
            
            # 更新配置项
            config_data["recent_time_filter"] = int(self.recent_time_filter_var.get())*60
            config_data["duration_filter"] = self.duration_filter_var.get()
            config_data["save_path"] = self.save_path_var.get()
            
            # 保存配置
            settings.update(config_data)
            
            self.log_message("设置已保存")
        except Exception as e:
            self.log_message(f"保存设置失败: {str(e)}")
    
    def open_project_folder(self):
        """打开项目存储目录"""
        import subprocess
        import platform
        
        # 直接读取配置文件获取save_path，避免调用异步方法
        from src.config.settings import Settings
        from src.tools import ColorfulConsole
        
        settings = Settings(PROJECT_ROOT, ColorfulConsole())
        config_data = settings.read()
        save_path = config_data.get("save_path", "")
        
        # 检查是否设置了自定义保存路径
        folder_path = str(PROJECT_ROOT)
        if save_path:
            folder_path = save_path
        
        system = platform.system()
        
        try:
            if system == "Windows":
                subprocess.Popen(f'explorer "{folder_path}"')
            elif system == "Darwin":  # macOS
                subprocess.Popen(["open", folder_path])
            else:  # Linux
                subprocess.Popen(["xdg-open", folder_path])
            
            self.log_message(f"已打开存储目录: {folder_path}")
        except Exception as e:
            self.log_message(f"打开存储目录失败: {str(e)}")
    
    def save_cookie_to_config(self, cookie_value):
        """将Cookie保存到配置中"""
        try:
            # 初始化TikTokDownloader实例以访问cookie对象
                downLoader = TikTokDownloader()
                downLoader.check_settings(False)
                # 验证并保存Cookie
                if downLoader.cookie.validate_cookie_minimal(cookie_value):
                    # 提取并保存Cookie
                    downLoader.cookie.extract(cookie_value, True, "cookie", "抖音")
                    self.log_message("关联数据已成功保存到配置文件中")                
                else:
                    self.log_message("关联数据格式验证失败")
                    return False
        except Exception as e:
            self.log_message(f"保存关联时出错: {str(e)}")
            return False
        
        return True
    
    def clear_log(self):
        """清空日志"""
        self.log_text.configure(state=tk.NORMAL)
        self.log_text.delete(1.0, tk.END)
        self.log_text.configure(state=tk.DISABLED)
    
    def log_message(self, message):
        """添加消息到日志"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        formatted_message = f"[{timestamp}] {message}\n"
        self.message_queue.put(formatted_message)
    
    def check_queue(self):
        """检查消息队列并更新UI"""
        try:
            while True:
                message = self.message_queue.get_nowait()
                # Handle None as a stop signal
                if message is None:
                    break
                self.log_text.configure(state=tk.NORMAL)
                self.log_text.insert(tk.END, message)
                self.log_text.configure(state=tk.DISABLED)
                self.log_text.see(tk.END)
        except Empty:
            pass
        
        # 每100毫秒检查一次队列
        self.root.after(100, self.check_queue)
    
    def _run_downloader(self):
        """在独立线程中运行下载器"""
        try:
            # 运行异步任务
            asyncio.run(self._async_run_downloader())
        except Exception as e:
            self.log_message(f"发生错误: {str(e)}")
        finally:
            if self.is_running:  # 如果仍在运行状态，则标记为停止
                self.is_running = False
                self.stop_event.clear()
    
    async def _async_run_downloader(self):
        """异步运行下载器"""
        try:
            async with TikTokDownloader() as app:
                # 保存实例引用以便停止时使用
                self.app_instance = app
                
                # 这里我们模拟运行，实际应用中可能需要调用特定功能
                self.log_message("初始化下载器...")
                app.check_config()
                # 替换日志记录器为GUI版本
                # 保存原始logger类
                original_logger = app.logger
                # 创建带有GUI回调的日志记录器
                def gui_logger_factory(main_path, console, root="", folder="", name=""):
                    return GUILogger(main_path, console, root, folder, name, log_callback=self.log_message)
                app.logger = gui_logger_factory
                await app.check_settings(
                    False,
                )
                # if await app.disclaimer():
                #     self.log_message("免责声明已确认")
                #     self.log_message("请在终端中进行操作，GUI目前仅提供开始/停止功能")
                #     # 等待直到收到停止信号
                while not self.stop_event.is_set() and app.running:
                    await app.runOnce(self.log_message,stop_event=self.stop_event)
                    # 检查是否设置了停止事件或应用已停止运行
                    if self.stop_event.is_set() or not app.running:
                        break
                    await asyncio.sleep(0.2)
                # else:
                #     self.log_message("需要同意免责声明才能继续")
        except Exception as e:
            self.log_message(f"运行出错: {str(e)}")
        finally:
            self.log_message("下载器已停止")
    
    def run(self):
        """运行GUI主循环"""
        self.root.mainloop()

    def set_cookie(self):
        """设置Cookie"""
        # 创建一个新窗口用于输入Cookie
        cookie_window = tk.Toplevel(self.root)
        cookie_window.title("设置关联")
        cookie_window.geometry("500x200")
        cookie_window.resizable(True, True)
        
        # Cookie输入框
        ttk.Label(cookie_window, text="请输入关联数据:").pack(pady=5)
        cookie_text = tk.Text(cookie_window, height=5, width=60)
        cookie_text.pack(pady=5, padx=10)
        
        def save_cookie():
            cookie_value = cookie_text.get("1.0", tk.END).strip()
            if cookie_value:
                # 验证Cookie格式
                from src.module.cookie import Cookie
                if Cookie.validate_cookie_minimal(cookie_value):
                    # 保存Cookie到配置                    
                    if self.save_cookie_to_config(cookie_value):
                        cookie_window.destroy()
                else:
                    self.log_message("关联数据格式无效，请检查后重新输入！")
            else:
                self.log_message("关联数据不能为空！")
        
        # 保存按钮
        save_button = ttk.Button(cookie_window, text="保存", command=save_cookie)
        save_button.pack(pady=5)
        
        # 说明文本
        info_label = ttk.Label(cookie_window, 
                              text="提示：请从工具中复制关联内容",
                              foreground="gray")
        info_label.pack(pady=5)
    
    def activate_license(self):
        """激活使用"""
        # 创建使用激活窗口
        auth_window = tk.Toplevel(self.root)
        auth_window.title("激活使用")
        auth_window.geometry("500x300")
        auth_window.resizable(False, False)
        
        # 居中显示
        auth_window.transient(self.root)
        auth_window.grab_set()
        
        # 机器码显示
        machine_code = self.get_machine_code()
        ttk.Label(auth_window, text=f"机器码: {machine_code}", foreground="blue").pack(pady=5)
        
        # 使用码输入
        ttk.Label(auth_window, text="请输入使用码:").pack(pady=(10, 0))
        license_entry = ttk.Entry(auth_window, width=50)
        license_entry.pack(pady=5)
        
         
        
        def verify_license():
            license_key = license_entry.get().strip()
            if not license_key:
                self.log_message("请输入使用码")
                return
            
            # 验证使用码（这里只是一个示例实现）
            if self.validate_license_key(license_key, machine_code):
                # 保存使用信息
                self.save_license_info(license_key)
                auth_window.destroy()
                
                # 更新使用状态
                self.is_authorized = True
                self.auth_label.config(text="使用状态: 已使用")  
                
                # 启用开始按钮
                self.start_button.config(state=tk.NORMAL)
                
                self.log_message("使用激活成功")
            else:
                self.log_message("使用码无效，请重新输入")
        
        # 按钮框架
        button_frame = ttk.Frame(auth_window)
        button_frame.pack(pady=20)
        ttk.Button(button_frame, text="激活", command=verify_license).pack(side=tk.LEFT, padx=5)         
        ttk.Button(button_frame, text="取消", command=auth_window.destroy).pack(side=tk.LEFT, padx=5)
    
    def validate_license_key(self, license_key, machine_code):
        """
        验证使用码（示例实现）
        实际应用中应该连接服务器验证或使用加密算法验证
        """
        # 这里只是一个简单的示例验证
        # 实际应用中应该有更复杂的验证逻辑
        expected_license = self.generate_license_key(machine_code,license_key.split("-LICENSE")[0])
        return license_key == expected_license
    
    def save_license_info(self, license_key):
        """
        保存使用信息
        """
        # 获取机器码
        machine_code = self.get_machine_code() 
        
        auth_data = {
            "license_key": license_key,
            "machine_code": machine_code,            
            "activated_at": datetime.now().isoformat()
        }
        
        auth_file = PROJECT_ROOT.joinpath("auth.key")
        with open(auth_file, 'w') as f:
            json.dump(auth_data, f, indent=2)
    
    def manage_accounts(self):
        """管理账号列表"""
        # 创建新窗口
        account_window = tk.Toplevel(self.root)
        account_window.title("监视账号列表管理")
        account_window.geometry("700x500")
        account_window.resizable(True, True)
        
        # 初始化选中索引跟踪变量
        self.selected_index = None
        
        # 创建主框架
        main_frame = ttk.Frame(account_window, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 说明标签
        info_label = ttk.Label(main_frame, text="在此添加需要监视的抖音账号URL，每行一个")
        info_label.pack(fill=tk.X, pady=(0, 10))
        
        # 创建列表框和滚动条
        list_frame = ttk.Frame(main_frame)
        list_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
        
        # 添加滚动条
        scrollbar_y = ttk.Scrollbar(list_frame, orient=tk.VERTICAL)
        scrollbar_y.pack(side=tk.RIGHT, fill=tk.Y)
        
        scrollbar_x = ttk.Scrollbar(list_frame, orient=tk.HORIZONTAL)
        scrollbar_x.pack(side=tk.BOTTOM, fill=tk.X)
        
        # 创建列表框
        self.account_listbox = tk.Listbox(list_frame, 
                                          yscrollcommand=scrollbar_y.set,
                                          xscrollcommand=scrollbar_x.set,
                                          selectmode=tk.SINGLE)  # 单选模式
        self.account_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar_y.config(command=self.account_listbox.yview)
        scrollbar_x.config(command=self.account_listbox.xview)
        
        # 创建输入框和按钮框架
        input_frame = ttk.Frame(main_frame)
        input_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 输入框
        ttk.Label(input_frame, text="账号地址:").pack(side=tk.LEFT)
        self.account_entry = ttk.Entry(input_frame)
        self.account_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(5, 5))
        
        # 按钮框架
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X)
        
        # 添加按钮
        add_button = ttk.Button(button_frame, text="添加", command=self.add_account)
        add_button.pack(side=tk.LEFT, padx=(0, 5))
        
        # 删除按钮
        delete_button = ttk.Button(button_frame, text="删除", command=self.delete_account)
        delete_button.pack(side=tk.LEFT, padx=(0, 5))
        
        # 修改按钮
        edit_button = ttk.Button(button_frame, text="修改", command=self.edit_account)
        edit_button.pack(side=tk.LEFT, padx=(0, 5))
        
        # 刷新按钮
        refresh_button = ttk.Button(button_frame, text="刷新", command=self.load_accounts)
        refresh_button.pack(side=tk.LEFT, padx=(0, 5))
        
        # 清除选择按钮
        clear_selection_button = ttk.Button(button_frame, text="清除选择", command=self.clear_selection)
        clear_selection_button.pack(side=tk.LEFT, padx=(0, 5))
        
        # 保存按钮
        save_button = ttk.Button(button_frame, text="保存", command=self.save_accounts)
        save_button.pack(side=tk.RIGHT)
        
        # 加载账号列表
        self.load_accounts()
        
        # 绑定列表框选择事件
        self.account_listbox.bind('<<ListboxSelect>>', self.on_account_select)
    
    def clear_selection(self):
        """清除列表框的选择"""
        self.account_listbox.selection_clear(0, tk.END)
        self.account_entry.delete(0, tk.END)
        self.selected_index = None  # 重置选中索引
        self.log_message("已清除选择")
    
    def load_accounts(self):
        """加载账号列表"""
        try:
            # 清空列表框
            self.account_listbox.delete(0, tk.END)
            
            # 重置选中索引
            self.selected_index = None
            
            # 读取账号列表文件
            list_file = PROJECT_ROOT.joinpath("list.txt")
            if list_file.exists():
                with open(list_file, 'r', encoding='utf-8') as f:
                    accounts = [line.strip() for line in f.readlines() if line.strip()]
                    for account in accounts:
                        self.account_listbox.insert(tk.END, account)
            else:
                # 如果文件不存在，创建空文件
                with open(list_file, 'w', encoding='utf-8') as f:
                    pass
                self.log_message("创建空的账号列表文件")
        except Exception as e:
            self.log_message(f"加载账号列表失败: {str(e)}")
    
    def add_account(self):
        """添加账号"""
        account = self.account_entry.get().strip()
        if account:
            self.account_listbox.insert(tk.END, account)
            self.account_entry.delete(0, tk.END)
            self.log_message("账号已添加")
        else:
            self.log_message("请输入有效的账号地址")
    
    def delete_account(self):
        """删除选中的账号"""
        try:
            # 使用跟踪的选中索引而不是重新获取选择
            if self.selected_index is not None:
                account = self.account_listbox.get(self.selected_index)
                self.account_listbox.delete(self.selected_index)
                # 重置选中索引
                self.selected_index = None
                self.account_entry.delete(0, tk.END)
                self.log_message(f"账号 '{account}' 已删除")
            else:
                self.log_message("请选择要删除的账号")
        except Exception as e:
            self.log_message(f"删除账号时出错: {str(e)}")
    
    def edit_account(self):
        """修改选中的账号"""
        try:
            # 使用跟踪的选中索引而不是重新获取选择
            if self.selected_index is not None:
                old_account = self.account_listbox.get(self.selected_index)
                account = self.account_entry.get().strip()
                if account:
                    # 删除选中的项并插入修改后的项
                    self.account_listbox.delete(self.selected_index)
                    self.account_listbox.insert(self.selected_index, account)
                    self.account_entry.delete(0, tk.END)
                    # 重置选中索引
                    self.selected_index = None
                    self.log_message(f"账号 '{old_account}' 已修改为 '{account}'")
                else:
                    self.log_message("请输入有效的账号地址")
            else:
                self.log_message("请选择要修改的账号")
        except Exception as e:
            self.log_message(f"修改账号时出错: {str(e)}")
    
    def on_account_select(self, event):
        """当选择账号时，将账号地址填充到输入框"""
        try:
            selection = self.account_listbox.curselection()
            if selection is not None and len(selection) > 0:
                self.selected_index = selection[0]  # 更新选中索引
                account = self.account_listbox.get(self.selected_index)
                self.account_entry.delete(0, tk.END)
                self.account_entry.insert(0, account)
        except Exception as e:
            self.log_message(f"选择账号时出错: {str(e)}")
    
    def save_accounts(self):
        """保存账号列表到文件"""
        try:
            list_file = PROJECT_ROOT.joinpath("list.txt")
            accounts = self.account_listbox.get(0, tk.END)
            with open(list_file, 'w', encoding='utf-8') as f:
                for account in accounts:
                    f.write(account + '\n')
            self.log_message("账号列表已保存")
        except Exception as e:
            self.log_message(f"保存账号列表失败: {str(e)}")

def gui_main():
    app = TikTokGUI()
    app.run()